#!/usr/bin/perl

######################################################################
package Swignition::uF::hReview;
######################################################################

use CGI::Util;
use Swignition::GenericParser::Utils;
use Swignition::uF;
use XML::LibXML qw(:all);
use strict;


sub consume
{
	my $page = shift;

	return unless ($page->{Profiles}->{'http://www.purl.org/stuff/rev#'}
		|| !$page->{options}->{uf_strict_profiles});

	$page->{uF}->{hReview} = [ parse_all($page) ];

	foreach my $xf (@{ $page->{uF}->{hReview} })
	{
		$page->mergeSubject( $xf->rdf_subject );
		$page->mergeSubject( $xf->rdf_subject_2 );

		foreach my $tag (@{$xf->{tag}})
		{
			my $tagging_subject = $tag->rdf_subject_tagging;
			if ($xf->{item}->{_implicit} && defined $xf->{'item'}->{'url'})
			{
				$tagging_subject->addVal('http://www.holygoat.co.uk/owl/redwood/0.1/tags/associatedResource',
					Swignition::RDFModel::Value->new($xf->{'item'}->{'url'}, 1));
			}
			else
			{
				$tagging_subject->addVal('http://www.holygoat.co.uk/owl/redwood/0.1/tags/associatedResource',
					Swignition::RDFModel::Value->new($xf->{'item'}->uri, 1));
			}
			$tagging_subject->addVal('http://purl.org/dc/terms/source',
				Swignition::RDFModel::Value->new($xf->uri, 1));
			if ($xf->{dtreviewed})
			{
				$tagging_subject->addVal("http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedOn",
					Swignition::RDFModel::Value->new($xf->{dtreviewed}));
			}
			foreach my $reviewer (@{$xf->{reviewer}})
			{
				$tagging_subject->addVal("http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedBy",
					Swignition::RDFModel::Value->new($reviewer->uri, 1));
			}

			$page->mergeSubject( $tagging_subject );
		}
		
		foreach my $r (@{ $xf->{rating} })
			{ $page->mergeSubject( $r->rdf_subject ); }
	}
	
	if (defined $page->{uF}->{hReview}->[0])
		{ $page->mergeNS( $page->{uF}->{hReview}->[0] ); }
}


sub parse_all
{
	my $page   = shift;
	my $within = shift || $page->{DOM};
	my @nodes  = searchClass('hreview', $within);
	my @rv;
	
	foreach my $xf (@nodes)
	{
		my $XF = parse($page, $xf);
		push @rv, $XF;
	}
	
	if ( $page->{Profiles}->{'http://microformats.org/wiki/xfolk-profile'}
	||  !$page->{options}->{uf_strict_profiles})
	{	
		my @nodes = searchClass('xfolkentry', $within);
		foreach my $xf (@nodes)
		{
			next if ($xf->getAttribute('class') =~ /\b(hreview)\b/);
			my $XF = parse($page, $xf);
			push @rv, $XF;
		}
	}
	
	return @rv;
	
} #/sub parse_all


sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;

	if ($this->{permalink})
	{
		push @rv, $this->{permalink};
	}
	if (length $this->{_id})
	{
		push @rv, $this->{_page}->uri.'#'.$this->{_id};
	}
	if (lc($this->{_dom}->tagName) eq 'body')
	{
		push @rv, $this->{_page}->uri;
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'Review')
		unless (@rv);
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
} #/sub uri


sub rdf_subject
{
	my $this    = shift;

	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->addObject($this);
	$subject->setDom($this->{_dom});
	
	my $val = Swignition::RDFModel::Value->new('http://www.purl.org/stuff/rev#Review', 1);
	$subject->addVal("http://www.w3.org/1999/02/22-rdf-syntax-ns#type", $val);

	if ($this->{version})
	{
		$val = Swignition::RDFModel::Value->new($this->{version});
		$subject->addVal("http://microformats.org/wiki/hReview#version", $val);
	}
	
	if ($this->{dtreviewed})
	{
		$val = Swignition::RDFModel::Value->new($this->{dtreviewed});
		$subject->addVal("http://purl.org/dc/terms/date", $val);
	}
	
	if ($this->{summary})
	{
		$val = Swignition::RDFModel::Value->new($this->{summary});
		$subject->addVal("http://www.purl.org/stuff/rev#title", $val);
		$subject->addVal("http://purl.org/dc/terms/title", $val);
	}
	
	if ($this->{type})
	{
		$val = Swignition::RDFModel::Value->new($this->{type});
		$subject->addVal("http://www.purl.org/stuff/rev#type", $val);
	}
	
	if ($this->{description})
	{
		foreach my $d (@{$this->{description}})
		{
			$val = Swignition::RDFModel::Value->new($d);
			$subject->addVal('http://purl.org/dc/terms/abstract', $val);
		}
		foreach my $d (@{$this->{html_description}})
		{
			$val = Swignition::RDFModel::Value->new($d);
			$val->setDatatype('http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral');
			$val->{xmlns} = 'http://www.w3.org/1999/xhtml';
			$subject->addVal('http://www.purl.org/stuff/rev#text', $val);
		}
	}

	if ($this->{item}->{_implicit})
	{
		$val = Swignition::RDFModel::Value->new($this->{item}->{uid}, 1);
#		$subject->addVal("http://microformats.org/wiki/hReview#item", $val);
		$subject->addVal("http://xmlns.com/foaf/0.1/primaryTopic", $val);		
	}
	elsif ($this->{item})
	{
		$val = Swignition::RDFModel::Value->new($this->{item}->uri, 1);
#		$subject->addVal("http://microformats.org/wiki/hReview#item", $val);
		$subject->addVal("http://xmlns.com/foaf/0.1/primaryTopic", $val);		
	}
	
	foreach my $r (@{ $this->{'reviewer'} })
	{
		$val = Swignition::RDFModel::Value->new($r->uri, 1);
		$subject->addVal("http://www.purl.org/stuff/rev#reviewer", $val);
		$subject->addVal("http://purl.org/dc/terms/creator", $val);
		$subject->addVal("http://xmlns.com/foaf/0.1/maker", $val);
	}
	
	foreach my $r (@{ $this->{'rating'} })
	{
		$val = Swignition::RDFModel::Value->new($r->uri, 1);
		$subject->addVal("http://www.purl.org/stuff/rev#rating", $val);
	}
	
	foreach my $V (@{ $this->{'license'} })
	{
		if (UNIVERSAL::can($V, 'uri'))
			{ $val = Swignition::RDFModel::Value->new($V->uri, 1); }
		else
			{ $val = Swignition::RDFModel::Value->new($V); }
		$subject->addVal('http://creativecommons.org/ns#license', $val);
		$subject->addVal('http://purl.org/dc/terms/license', $val);
	}
	
	return $subject;
	
} #/sub rdf_subject


sub rdf_subject_2
{
	my $this    = shift;
	my $subject;
	
	if ($this->{item}->{_implicit})
	{
		$subject = Swignition::RDFModel::Subject->new($this->{item}->{uid});

		my $val = Swignition::RDFModel::Value->new($this->{item}->{fn});
		$subject->addVal("http://purl.org/dc/terms/title", $val);

		if ($this->{item}->{url})
		{
			$val = Swignition::RDFModel::Value->new($this->{item}->{url}, 1);
			$subject->addVal("http://xmlns.com/foaf/0.1/page", $val);
		}

		if ($this->{item}->{photo})
		{
			$val = Swignition::RDFModel::Value->new($this->{item}->{photo}, 1);
			$subject->addVal("http://xmlns.com/foaf/0.1/img", $val);
		}
	}
	else
	{
		$subject = Swignition::RDFModel::Subject->new($this->{item}->uri);
	}
			
	my $val = Swignition::RDFModel::Value->new($this->uri, 1);
	$subject->addVal("http://www.purl.org/stuff/rev#hasReview", $val);

	foreach my $tag (@{$this->{tag}})
	{
		$subject->addVal('http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag',
			Swignition::RDFModel::Value->new($tag->uri, 1));
		$subject->addVal('http://www.holygoat.co.uk/owl/redwood/0.1/tags/tag',
			Swignition::RDFModel::Value->new($tag->uri_tagging, 1));
	}

	return $subject;
} #/sub rdf_subject_2


sub metadata_ns
{
	my $this = shift;
	return {
		'rev' => {
				nsurl=>'http://www.purl.org/stuff/rev#',
				title=>'RDF Review Vocab'
			},
		'hrev' => {
				nsurl=>'http://microformats.org/wiki/hReview#',
				title=>'hReview'
			}
		};
} #/sub metadata_ns


sub parse
{
	my $page       = shift;
	my $rv         = { '_dom'=>shift };
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $hrev_node  = $rv->{'_dom'}->cloneNode(1);
	
	my $id = $hrev_node->getAttribute('id');
	$rv->{_id} = $id if (length $id);
	$rv->{_page} = $page;
		
	data_patterns($page, $hrev_node, 6);

	# Handle xFolk.
	if ($hrev_node->getAttribute('class') =~ /\b(xfolkentry)\b/)
	{
		my @tl = Swignition::GenericParser::Utils::searchClass('taggedlink', $hrev_node);
		if ($tl[0])
		{
			my $link_url;

			if ($tl[0]->localname eq 'a' || $tl[0]->localname eq 'area')
				{ $link_url = $page->uri($tl[0]->getAttribute('href')); }
			elsif ($tl[0]->localname eq 'img')
				{ $link_url = $page->uri($tl[0]->getAttribute('src')); }
			elsif ($tl[0]->localname eq 'object')
				{ $link_url = $page->uri($tl[0]->getAttribute('data')); }

			$rv->{item}->{_implicit} = 1;
			$rv->{item}->{fn}  = Swignition::GenericParser::Utils::STRINGIFY($tl[0], 'value');
			$rv->{item}->{url} = $link_url;
		}
	}

	# Note: for item we try hAudio first, as it will likely contain an hCard,
	# Then hEvent, as it may contain an hCard. Lastly try hCard, as it's unlikely
	# to contain anything else.
	simple_parse(
		$rv,
		$hrev_node,
		[
			['reviewer',    'M*',   {'embedded'=>'hCard'}],
			['item',        'm1',   {'embedded'=>'hAudio hEvent hCard'}], # lowercase 'm' = don't try plain string.
			['version',     'n?'],
			['summary',     '1'],
			['type',        '?'],
			['bookmark',    'ru?',  {'use-key'=>'permalink'}],
			['description', 'H*'],
			['dtreviewed',  'd?'],
		],
		{
			'rel-license'  => 'license',
			'rel-tag'      => 'tag'
		}
	);
	
	# If no "reviewer" is found inside the hReview, parsers should look 
	# outside the hReview, in the context of the page, for the "reviewer". 
	# If there is no "reviewer" outside either, then parsers should use the
	# author defined by the containing document language, e.g. for HTML
	# documents, the <address> contact info for the page (which is ideally
	# marked up as an hCard as well)
	unless ($rv->{reviewer})
	{
		my @external_reviewers = searchClass('reviewer', $page->{DOM}->documentElement);
		my @addresses = $page->{DOM}->getElementsByTagName('address');
		push @external_reviewers, @addresses;
		
		POSSIBILITY: foreach my $n (@external_reviewers)
		{
			if ($n->getAttribute('class') =~ /\b(vcard)\b/)
			{
				push @{$rv->{reviewer}}, Swignition::uF::hCard::parse($page, $n);
				last POSSIBILITY;
			}
			else
			{
				my @hcards = Swignition::uF::hCard::parse_all($page, $n);
				if (defined $hcards[0])
				{
					push @{$rv->{reviewer}}, $hcards[0];
					last POSSIBILITY;
				}
			}
		}
	}

	# Item, if not already set.
	my @items = Swignition::GenericParser::Utils::searchClass('item', $hrev_node);
	if ($items[0] && !$rv->{item})
	{
		my $item = $items[0];
		my ($item_fn, $item_url, $item_photo);

		my @n = Swignition::GenericParser::Utils::searchClass('fn', $item);
		if (@n)
		{
			$item_fn = Swignition::GenericParser::Utils::STRINGIFY($n[0], 'value');

			@n = Swignition::GenericParser::Utils::searchClass('url', $item);
			if (@n)
			{
				if (length $n[0]->getAttribute('href'))
					{ $item_url = $page->uri($n[0]->getAttribute('href')); }
				elsif (length $n[0]->getAttribute('data'))
					{ $item_url = $page->uri($n[0]->getAttribute('data')); }
				elsif (length $n[0]->getAttribute('src'))
					{ $item_url = $page->uri($n[0]->getAttribute('src')); }
				else
					{ $item_url = Swignition::GenericParser::Utils::STRINGIFY($n[0]); }
			}

			@n = Swignition::GenericParser::Utils::searchClass('photo', $item);
			if (@n)
			{
				if (length $n[0]->getAttribute('src'))
					{ $item_photo = $page->uri($n[0]->getAttribute('src')); }
				elsif (length $n[0]->getAttribute('data'))
					{ $item_photo = $page->uri($n[0]->getAttribute('data')); }
				elsif (length $n[0]->getAttribute('href'))
					{ $item_photo = $page->uri($n[0]->getAttribute('href')); }
				else
					{ $item_photo = Swignition::GenericParser::Utils::STRINGIFY($n[0]); }
			}
		}
		
		else
		{
			$item_fn = Swignition::GenericParser::Utils::STRINGIFY($item, 'value');
			$item_url = $page->uri($item->getAttribute('href'))
				if (length $item->getAttribute('href'));
			$item_photo = $page->uri($item->getAttribute('data'))
				if (length $item->getAttribute('data'));
			$item_photo = $page->uri($item->getAttribute('src'))
				if (length $item->getAttribute('src'));
		}
		
		$rv->{item}->{_implicit} = 1;
		$rv->{item}->{fn} = $item_fn if ($item_fn);
		$rv->{item}->{url} = $item_url if ($item_url);
		$rv->{item}->{photo} = $item_photo if ($item_photo);

		$rv->{item}->{uid} = Swignition::GenericParser::Utils::BNodeURI($item, 'Item');
		$rv->{item}->{uid} = $page->uri.'#'.$item->getAttribute('id')
			if ($item->getAttribute('id'));
		$rv->{item}->{uid} = $item_url if ($item_url);
	}
	
	# Default version
	$rv->{version} = '0.3' unless ($rv->{version});
	
	# Auto-detected type
	if (length $rv->{item}->{kind} && !length $rv->{type})
	{
		$rv->{type} = $rv->{item}->{kind};
		$rv->{type} = 'person'   if ($rv->{type} eq 'individual');
		$rv->{type} = 'business' if ($rv->{type} eq 'org');
		$rv->{type} = 'business' if ($rv->{type} eq 'group');
		$rv->{type} = 'place'    if ($rv->{type} eq 'x-post-office-box'
		                         ||  $rv->{type} eq 'x-extended-address'
		                         ||  $rv->{type} eq 'x-street-address'
		                         ||  $rv->{type} eq 'x-locality'
		                         ||  $rv->{type} eq 'x-region'
		                         ||  $rv->{type} eq 'x-country-name'
		                         ||  $rv->{type} eq 'x-postal-code');
	}
	elsif (length $rv->{item}->{haudio_type} && !length $rv->{type})
	{
		$rv->{type} = 'product';
	}

	# Rating (worst, best, value, rel-tag).
	my @ratings = Swignition::GenericParser::Utils::searchClass('rating', $hrev_node);
	foreach my $r (@ratings)
	{
		push @{$rv->{rating}}, Swignition::uF::hReview::Rating::parse($page, $r);
	}

	bless $rv;
	return $rv;
	
} #/sub parse


1; #/package Swignition::uF::hReview


package Swignition::uF::hReview::Rating;

sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;

	if (length $this->{_id})
	{
		push @rv, $this->{_page}->uri . '#' . $this->{taggedlink};
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'ReviewRating')
		unless (@rv);
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
} #/sub uri

sub rdf_subject
{
	my $this    = shift;

	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->addObject($this);
	$subject->setDom($this->{_dom});
	
	my $val = Swignition::RDFModel::Value->new('http://microformats.org/wiki/hReview#Rating', 1);
	$subject->addVal("http://www.w3.org/1999/02/22-rdf-syntax-ns#type", $val);

	$val = Swignition::RDFModel::Value->new($this->{'value'});
	$subject->addVal("http://www.w3.org/1999/02/22-rdf-syntax-ns#value", $val);

	$val = Swignition::RDFModel::Value->new($this->{'best'});
	$subject->addVal('http://www.purl.org/stuff/rev#maxRating', $val);

	$val = Swignition::RDFModel::Value->new($this->{'worst'});
	$subject->addVal('http://www.purl.org/stuff/rev#minRating', $val);
	
	foreach my $V (@{ $this->{'tag'} })
	{
		if (UNIVERSAL::can($V, 'uri'))
			{ $val = Swignition::RDFModel::Value->new($V->uri, 1); }
		else
			{ $val = Swignition::RDFModel::Value->new($V); }
		$subject->addVal('http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag', $val);
	}
	
	return $subject;
	
} #/sub rdf_subject

sub parse
{
	my $page  = shift;
	my $rv    = { '_dom'=>shift };
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $node  = $rv->{'_dom'};
	
	my $id = $node->getAttribute('id');
	$rv->{_id} = $id if (length $id);
	$rv->{_page} = $page;
	
	my $parent = $node;
	while ((defined $parent) && (ref $parent) && ($parent->nodeType == XML::LibXML::XML_ELEMENT_NODE))
	{
		last if ($parent->getAttribute('rel') =~ /\b(tag)\b/i);
		$parent = $parent->parentNode;
	}
	$parent = undef
		unless ($parent->nodeType == XML::LibXML::XML_ELEMENT_NODE 
		&&      $parent->getAttribute('rel') =~ /\b(tag)\b/i);
	
	my $root_node = $parent || $node;

	# Value		
	$rv->{value} = Swignition::GenericParser::Utils::STRINGIFY($node, 'value');

	# Worst and best
	my @n = Swignition::GenericParser::Utils::searchClass('worst', $root_node);
	$rv->{worst} = Swignition::GenericParser::Utils::STRINGIFY($n[0], undef, 1);
	$rv->{worst} = 0 unless ($rv->{worst});
	my @n = Swignition::GenericParser::Utils::searchClass('best', $root_node);
	$rv->{best} = Swignition::GenericParser::Utils::STRINGIFY($n[0], undef, 1);
	$rv->{best} = 5 unless ($rv->{best});

	# Categories
	if ($parent)
	{
		my $tag = Swignition::uF::RelTag::parse($page, $parent);
		$rv->{tag} = [$tag];
	}
	else
	{
		my @tags = Swignition::uF::RelTag::parse_all($page, $node);
		$rv->{tag} = \@tags unless (!@tags);
	}
	
	bless $rv;
	return $rv;
}

1; #/package Swignition::uF::hReview::Rating
